#include "./BSP/ADC/bsp_adc.h"
#include <stdio.h>

/* -------------------------------------------- 全局变量定义 -------------------------------------------- */

ADC_HandleTypeDef g_adc_handle;         /* ADC句柄 */

/* 单通道ADC采集 DMA读取 */
DMA_HandleTypeDef g_dma_adc_handle;     /* 定义要搬运ADC数据的DMA句柄 */
ADC_HandleTypeDef g_adc_dma_handle;     /* 定义ADC（DMA读取）句柄 */
uint8_t g_adc_dma_sta = 0;              /* DMA传输状态标志, 0,未完成; 1, 已完成 */

/* ADC数据存储 */
uint16_t g_adc_values[ADC_CH_NUM];      /* 存储各通道ADC原始值 */
float g_adc_voltages[ADC_CH_NUM];       /* 存储各通道电压值 */
float g_vdda_voltage = 3.3f;            /* 存储VDDA电压值，默认为3.3V */

/* ADC DMA缓冲区 */
#define ADC_DMA_BUF_SIZE        50 * ADC_CH_NUM /* ADC DMA采集 BUF大小，应等于ADC通道数的整数倍 */
uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];       /* ADC DMA BUF */

/* --------------------------------------------------- ADC基本操作 --------------------------------------------------- */

/**
 * @brief       ADC初始化函数
 * @param       无
 * @retval      无
 * @note        本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
 *              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期
 *              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us
 */
void adc_init(void)
{
    g_adc_handle.Instance = ADC_ADCX;                        /* 选择哪个ADC */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       /* 数据对齐方式：右对齐 */
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;       /* 非扫描模式，仅用到一个通道 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;          /* 关闭连续转换模式 */
    g_adc_handle.Init.NbrOfConversion = 1;                   /* 赋值范围是1~16，本实验用到1个规则通道序列 */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;       /* 禁止规则通道组间断模式 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;               /* 配置间断模式的规则通道个数，禁止规则通道组间断模式后，此参数忽略 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式：软件触发 */
    HAL_ADC_Init(&g_adc_handle);                             /* 初始化 */

    HAL_ADCEx_Calibration_Start(&g_adc_handle);              /* 校准ADC */
}

/**
 * @brief       ADC底层驱动，引脚配置，时钟使能
 * @param       hadc: ADC句柄
 * @retval      无
 * @note        此函数会被HAL_ADC_Init()函数调用
 */
//void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
//{
//    if(hadc->Instance == ADC_ADCX)
//    {
//        GPIO_InitTypeDef gpio_init_struct;
//        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
//        
//        ADC_ADCX_CHY_CLK_ENABLE();                                /* 使能ADCx时钟 */
//        ADC_ADCX_CHY_GPIO_CLK_ENABLE();                           /* 开启GPIO时钟 */

//        /* 设置ADC时钟 */
//        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;    /* ADC外设时钟 */
//        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;       /* 分频因子6时钟为72M/6=12MHz */
//        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                 /* 设置ADC时钟 */

//        /* 设置AD采集通道对应IO引脚工作模式 */
//        gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;             /* ADC通道IO引脚 */
//        gpio_init_struct.Mode = GPIO_MODE_ANALOG;                 /* 模拟 */
//        HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);
//    }
//}

/**
 * @brief       设置ADC通道采样时间
 * @param       adc_handle: ADC句柄指针
 * @param       ch: 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17
 * @param       rank: 多通道采集时需要设置的采集编号
 * @param       stime: 采样时间 0~7
 * @retval      无
 * @note        采样时间对应关系:
 *              - ADC_SAMPLETIME_1CYCLE_5: 1.5个ADC时钟周期
 *              - ADC_SAMPLETIME_7CYCLES_5: 7.5个ADC时钟周期
 *              - ADC_SAMPLETIME_13CYCLES_5: 13.5个ADC时钟周期
 *              - ADC_SAMPLETIME_28CYCLES_5: 28.5个ADC时钟周期
 *              - ADC_SAMPLETIME_41CYCLES_5: 41.5个ADC时钟周期
 *              - ADC_SAMPLETIME_55CYCLES_5: 55.5个ADC时钟周期
 *              - ADC_SAMPLETIME_71CYCLES_5: 71.5个ADC时钟周期
 *              - ADC_SAMPLETIME_239CYCLES_5: 239.5个ADC时钟周期
 *              编号范围: ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16
 */
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    ADC_ChannelConfTypeDef adc_ch_conf;
    
    adc_ch_conf.Channel = ch;                               /* 通道 */
    adc_ch_conf.Rank = rank;                                /* 序列 */
    adc_ch_conf.SamplingTime = stime;                       /* 采样时间 */
    HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);        /* 通道配置 */
}

/**
 * @brief       获得ADC转换后的结果
 * @param       ch: 通道值, 取值范围为ADC_CHANNEL_0~ADC_CHANNEL_17
 * @retval      ADC转换结果
 */
uint32_t adc_get_result(uint32_t ch)
{
    adc_channel_set(&g_adc_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道，序列和采样时间 */

    HAL_ADC_Start(&g_adc_handle);                           /* 开启ADC */
    HAL_ADC_PollForConversion(&g_adc_handle, 10);           /* 轮询转换 */
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);       /* 返回最近一次ADC规则组的转换结果 */
}

/**
 * @brief       获取通道ch的转换值，取times次,然后平均
 * @param       ch: 通道号, 0~17
 * @param       times: 获取次数
 * @retval      通道ch的times次转换结果平均值
 */
uint32_t adc_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;
    uint8_t t;

    for (t = 0; t < times; t++)     /* 获取times次数据 */
    {
        temp_val += adc_get_result(ch);
        HAL_Delay(5);
    }

    return temp_val / times;        /* 返回平均值 */
}

/* ------------------------------------------------ 单通道ADC采集(DMA读取) ------------------------------------------------ */

/**
 * @brief       ADC DMA读取 初始化函数
 * @param       mar: 存储器地址
 * @retval      无
 * @note        本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置
 */
void adc_dma_init(uint32_t mar)
{
    GPIO_InitTypeDef gpio_init_struct;
    RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
    ADC_ChannelConfTypeDef adc_ch_conf = {0};

    ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */
    ADC_ADCX_CHY_GPIO_CLK_ENABLE();                                         /* 开启GPIO时钟 */

    if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                 /* 大于DMA1_Channel7, 则为DMA2的通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                                        /* DMA2时钟使能 */
    }
    else
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                                        /* DMA1时钟使能 */
    }

    /* 设置ADC时钟 */
    adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                  /* ADC外设时钟 */
    adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                     /* 分频因子6时钟为72M/6=12MHz */
    HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                               /* 设置ADC时钟 */

    /* 设置AD采集通道对应IO引脚工作模式 */
    gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;                           /* ADC通道对应的IO引脚 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;                               /* 模拟 */
    HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);

    /* 初始化DMA */
    g_dma_adc_handle.Instance = ADC_ADCX_DMACx;                             /* 设置DMA通道 */
    g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 从外设到存储器模式 */
    g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
    g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
    g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
    g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
    g_dma_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */
    g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
    HAL_DMA_Init(&g_dma_adc_handle);

    __HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);         /* 将DMA与adc联系起来 */

    g_adc_dma_handle.Instance = ADC_ADCX;                                   /* 选择哪个ADC */
    g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                  /* 数据对齐方式：右对齐 */
    g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;                  /* 非扫描模式，仅用到一个通道 */
    g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;                      /* 使能连续转换模式 */
    g_adc_dma_handle.Init.NbrOfConversion = 1;                              /* 赋值范围是1~16，本实验用到1个规则通道序列 */
    g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;                  /* 禁止规则通道组间断模式 */
    g_adc_dma_handle.Init.NbrOfDiscConversion = 0;                          /* 配置间断模式的规则通道个数，禁止规则通道组间断模式后，此参数忽略 */
    g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;            /* 触发转换方式：软件触发 */
    HAL_ADC_Init(&g_adc_dma_handle);                                        /* 初始化 */

    HAL_ADCEx_Calibration_Start(&g_adc_dma_handle);                         /* 校准ADC */

    /* 配置ADC通道 */
    adc_ch_conf.Channel = ADC_ADCX_CHY;                                     /* 通道 */
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                  /* 序列 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                  /* 采样时间，设置最大采样周期:239.5个ADC周期 */
    HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);                 /* 通道配置 */

    /* 配置DMA数据流请求中断优先级 */
    HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);
    HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);

    HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);       /* 启动DMA，并开启中断 */
    HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar, 0);                          /* 开启ADC，通过DMA传输结果 */
}

/* ------------------------------------------------ 多通道ADC采集(DMA读取) ------------------------------------------------ */

DMA_HandleTypeDef g_dma_nch_adc_handle = {0};                               /* 定义要搬运ADC多通道数据的DMA句柄 */
ADC_HandleTypeDef g_adc_nch_dma_handle = {0};                               /* 定义ADC（多通道DMA读取）句柄 */

/**
 * @brief       ADC 8通道(PA0-PA3,PA6-PA7,PC4和内部参考电压) DMA读取初始化函数
 * @param       mar: 存储器地址，用于存储DMA传输的ADC转换结果
 * @retval      无
 * @note        本函数使用adc_init对ADC进行基本配置，并针对多通道进行特殊配置
 *              使用DMA连续采样三个通道:
 *              - ADC_CHANNEL_14 (PC4): 模拟信号采集
 *              - ADC_TEMPSENSOR_CHX: 内部温度传感器
 *              - ADC_VREFINT_CHX: 内部参考电压通道
 *              跳过PA0-PA7（用作按键、DAC输出、SPI等）
 *              使用 ADC_ADCX(默认=ADC1) 和 ADC_ADCX_DMACx(DMA1_Channel1)
 */
void adc_nch_dma_init(uint32_t mar)
{
    GPIO_InitTypeDef gpio_init_struct;
    RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
    ADC_ChannelConfTypeDef adc_ch_conf = {0};

    ADC_ADCX_CHY_CLK_ENABLE();                                                  /* 使能ADCx时钟 */
    __HAL_RCC_GPIOC_CLK_ENABLE();                                               /* 开启GPIOC时钟 */

    if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                     /* 大于DMA1_Channel7, 则为DMA2的通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                                            /* DMA2时钟使能 */
    }
    else
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                                            /* DMA1时钟使能 */
    }

    /* 设置ADC时钟 */
    adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                      /* ADC外设时钟 */
    adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                         /* 分频因子6时钟为72M/6=12MHz */
    HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                                   /* 设置ADC时钟 */
    
    /* 设置PC4对应的IO口模拟输入 - PC4对应 ADC1_IN14 */
    gpio_init_struct.Pin = GPIO_PIN_4;                                          /* GPIOC4 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;                                   /* 模拟 */
    HAL_GPIO_Init(GPIOC, &gpio_init_struct);

    /* 初始化DMA */
    g_dma_nch_adc_handle.Instance = ADC_ADCX_DMACx;                             /* 设置DMA通道 */
    g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 从外设到存储器模式 */
    g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
    g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
    g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
    g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
    g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */
    g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
    HAL_DMA_Init(&g_dma_nch_adc_handle);

    __HAL_LINKDMA(&g_adc_nch_dma_handle, DMA_Handle, g_dma_nch_adc_handle);     /* 将DMA与adc联系起来 */

    /* 初始化ADC */
    g_adc_nch_dma_handle.Instance = ADC_ADCX;                                   /* 选择哪个ADC */
    g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                  /* 数据对齐方式：右对齐 */
    g_adc_nch_dma_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;                   /* 使能扫描模式 */
    g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;                      /* 使能连续转换 */
    g_adc_nch_dma_handle.Init.NbrOfConversion = 3;                              /* 赋值范围是1~16，本次用到3个规则通道序列 */
    g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;                  /* 禁止规则通道组间断模式 */
    g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0;                          /* 配置间断模式的规则通道个数，禁止规则通道组间断模式后，此参数忽略 */
    g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;            /* 软件触发 */
    HAL_ADC_Init(&g_adc_nch_dma_handle);                                        /* 初始化 */

    SET_BIT(g_adc_nch_dma_handle.Instance->CR2, ADC_CR2_TSVREFE);               /* 使能内部参考电压 */
    HAL_ADCEx_Calibration_Start(&g_adc_nch_dma_handle);                         /* 校准ADC */

    /* 配置ADC通道 */
    /* 配置ADC通道1 - PC4 */
    adc_ch_conf.Channel = ADC_CHANNEL_14;                                       /* 配置使用的ADC通道 - PC4 */
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                      /* 采样序列里的第1个 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                      /* 采样时间，设置最大采样周期:239.5个ADC周期 */
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);                 /* 配置ADC通道 */

    /* 配置ADC通道2 - 内部温度传感器通道 */
    adc_ch_conf.Channel = ADC_TEMPSENSOR_CHX;                                   /* 内部温度传感器通道 */
    adc_ch_conf.Rank = ADC_REGULAR_RANK_2;                                      /* 采样序列里的第2个 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                      /* 采样时间，设置最大采样周期:239.5个ADC周期 */
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);                 /* 配置ADC通道 */

    /* 配置ADC通道3 - 内部参考电压通道 */
    adc_ch_conf.Channel = ADC_VREFINT_CHX;                                      /* 内部参考电压通道 */
    adc_ch_conf.Rank = ADC_REGULAR_RANK_3;                                      /* 采样序列里的第3个 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                      /* 采样时间，设置最大采样周期:239.5个ADC周期 */
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);                 /* 配置ADC通道 */

    /* 配置DMA数据流请求中断优先级 */
    HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);
    HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);

    HAL_DMA_Start_IT(&g_dma_nch_adc_handle, (uint32_t)&ADC1->DR, mar, 0);       /* 启动DMA，并开启中断 */
    HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar, 0);                          /* 开启ADC，通过DMA传输结果 */
}

/* -------------------------------------- 单通道ADC采集(DMA读取)和多通道ADC采集(DMA读取) ------------------------------------ */

/**
 * @brief       使能一次ADC DMA传输
 * @param       cndtr: DMA传输的次数
 * @retval      无
 * @note        该函数用寄存器来操作，防止用HAL库操作对其他参数有修改,也为了兼容性
 */
void adc_dma_enable(uint16_t cndtr)
{
    ADC_ADCX->CR2 &= ~(1 << 0);                 /* 先关闭ADC */

    ADC_ADCX_DMACx->CCR &= ~(1 << 0);           /* 关闭DMA传输 */
    while (ADC_ADCX_DMACx->CCR & (1 << 0));     /* 确保DMA可以被设置 */
    ADC_ADCX_DMACx->CNDTR = cndtr;              /* DMA传输数据量 */
    ADC_ADCX_DMACx->CCR |= 1 << 0;              /* 开启DMA传输 */

    ADC_ADCX->CR2 |= 1 << 0;                    /* 重新启动ADC */
    ADC_ADCX->CR2 |= 1 << 22;                   /* 启动规则转换通道 */
}

/**
 * @brief       ADC DMA采集中断服务函数
 * @param       无
 * @retval      无
 */
void ADC_ADCX_DMACx_IRQHandler(void)
{
    if (ADC_ADCX_DMACx_IS_TC())
    {
        g_adc_dma_sta = 1;                      /* 标记DMA传输完成 */
        ADC_ADCX_DMACx_CLR_TC();                /* 清除DMA1 数据流7 传输完成中断 */
    }
}

/* ------------------------------------------------ 内部温度传感器 ------------------------------------------------ */

/**
 * @brief       ADC内部温度传感器初始化函数
 * @param       无
 * @retval      无
 * @note        本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置
 *              STM32F103内部温度传感器只连接在ADC1的通道16上, 其他ADC无法进行转换
 */
void adc_temperature_init(void)
{
    adc_init();                                            /* 先初始化ADC */
    SET_BIT(g_adc_handle.Instance->CR2, ADC_CR2_TSVREFE);  /* TSVREFE = 1, 启用内部温度传感器和Vrefint */
}

/**
 * @brief       获取内部温度传感器温度值
 * @param       无
 * @retval      温度值(扩大了100倍,单位:℃)
 */
short adc_get_temperature(void)
{
    uint32_t adcx;
    short result;
    double temperature;

    adcx = adc_get_result_average(ADC_TEMPSENSOR_CHX, 20);  /* 读取内部温度传感器通道,10次取平均 */
    temperature = (float)adcx * (3.3 / 4096);               /* 转化为电压值 */
    temperature = (1.43 - temperature) / 0.0043 + 25;       /* 计算温度 */
    result = temperature *= 100;                            /* 扩大100倍 */
    return result;
}

/**
 * @brief       从DMA缓冲区获取温度传感器温度值
 * @param       无
 * @retval      温度值(扩大了100倍,单位:℃)
 * @note        使用DMA采集的温度传感器数据计算温度
 */
short adc_get_temperature_from_dma(void)
{
    uint16_t temp_adc;
    float temperature;
    short result;
    
    /* 获取温度传感器的ADC值 */
    temp_adc = g_adc_values[ADC_CHANNEL_TEMPSENSOR_INDEX];
    
    /* 转换为电压值 */
    temperature = (float)temp_adc * (g_vdda_voltage / 4096.0f);
    
    /* 计算温度：T = (1.43 - V) / 0.0043 + 25 */
    temperature = (1.43f - temperature) / 0.0043f + 25.0f;
    
    /* 扩大100倍并返回 */
    result = (short)(temperature * 100);
    
    return result;
}

/* ------------------------------------------------ ADC任务和数据处理 ------------------------------------------------ */

/**
 * @brief       ADC任务初始化函数
 * @param       无
 * @retval      无
 */
void adc_task_init(void)
{
    /* 初始化ADC DMA采集 */
    adc_nch_dma_init((uint32_t)&g_adc_dma_buf);
    
    /* 启动ADC DMA采集 */
    adc_dma_enable(ADC_DMA_BUF_SIZE);
}

/**
 * @brief       一阶低通滤波函数
 * @param       new_value: 新采集的数据值
 * @param       channel: 通道号，用于区分不同通道的滤波状态
 * @retval      滤波后的值
 * @note        滤波公式: y(n) = α·x(n) + (1-α)·y(n-1)
 *              内部使用静态数组保存各通道上次滤波结果
 *              滤波系数alpha=0.2，可根据需要调整
 */
uint16_t adc_filter(uint16_t new_value, uint8_t channel)
{
    /* 静态变量，保存各通道的滤波状态 */
    static uint16_t last_values[ADC_CH_NUM] = {0};   /* 上一次的滤波结果 */
    static uint8_t is_initialized = 0;               /* 初始化标志 */
    const float alpha = 0.2f;                        /* 滤波系数，值越小滤波效果越强，但响应越慢 */
    
    /* 检查通道号是否有效 */
    if (channel >= ADC_CH_NUM)
    {
        return new_value; /* 通道号无效，直接返回原值 */
    }
    
    /* 第一次调用时，初始化所有通道的last_values */
    if (!is_initialized)
    {
        for (uint8_t i = 0; i < ADC_CH_NUM; i++)
        {
            last_values[i] = 0;
        }
        is_initialized = 1;
        
        /* 第一次运行，直接使用当前值 */
        last_values[channel] = new_value;
        return new_value;
    }
    
    /* 一阶低通滤波: y(n) = α·x(n) + (1-α)·y(n-1) */
    float filtered_value = alpha * (float)new_value + (1.0f - alpha) * (float)last_values[channel];
    
    /* 更新该通道的last_value */
    last_values[channel] = (uint16_t)filtered_value;
    
    return (uint16_t)filtered_value;
}

/**
 * @brief       ADC数据处理函数
 * @param       无
 * @retval      1: 处理了新数据; 0: 没有新数据
 * @note        处理ADC采集到的数据，计算VDDA和各通道电压值
 *              通道0-5(PA0-PA5): 用于测量模拟信号，电压范围0-3.3V
 *              通道VREFINT: 内部参考电压，用于校准VDDA值
 */
uint8_t adc_process_data(void)
{
    uint16_t i, j;
    uint32_t sum;
    
    /* 判断是否有新数据 */
    if (g_adc_dma_sta == 1)
    {
        /* 首先计算各通道的原始ADC值并进行滤波 */
        for(j = 0; j < ADC_CH_NUM; j++)
        {
            sum = 0; /* 清零 */
            for (i = 0; i < ADC_DMA_BUF_SIZE / ADC_CH_NUM; i++)
            {
                sum += g_adc_dma_buf[(ADC_CH_NUM * i) + j];  /* 相同通道的转换数据累加 */
            }
            
            g_adc_values[j] = sum / (ADC_DMA_BUF_SIZE / ADC_CH_NUM);  /* 取平均值 */
            
            /* 对ADC值进行滤波处理 */
            g_adc_values[j] = adc_filter(g_adc_values[j], j);
        }
        
        /* 通过内部参考电压校准VDDA值: VDDA = 1.2V（典型值） * 4096 / VREFINT_ADC_VALUE */
        g_vdda_voltage = 1.2f * 4096.0f / (float)g_adc_values[ADC_CHANNEL_VREFINT_INDEX];
        
        /* 计算各通道电压值 */
        for (j = 0; j < ADC_CH_NUM; j++)
        {
            /* 计算基础电压值 - 存入g_adc_voltages */
            g_adc_voltages[j] = (float)g_adc_values[j] * (g_vdda_voltage / 4096);
        }
        
        /* 处理完成后，重新启动DMA采集 */
        g_adc_dma_sta = 0;  /* 清除DMA采集完成状态标志 */
        adc_dma_enable(ADC_DMA_BUF_SIZE);   /* 启动下一次ADC DMA多通道采集 */
        
        return 1; /* 返回1表示处理了新数据 */
    }
    
    return 0; /* 返回0表示没有新数据 */
}

/**
 * @brief       ADC扫描任务
 * @param       无
 * @retval      无
 * @note        通过串口打印各通道ADC值，用于无屏幕调试
 */
void adc_scan_task(void)
{
    /* 处理ADC数据 */
    if (adc_process_data())
    {
        printf("\r\n------ ADC Channel Values ------\r\n");
        
        /* 显示各通道电压值 */
        /* PC4 */
        printf("CH14(PC4): %.3fV\r\n", g_adc_voltages[ADC_CHANNEL_14_INDEX]);
         
        /* 显示VDDA电压 */
        printf("VDDA     : %.3fV\r\n", g_vdda_voltage);
        
        /* 显示内部温度传感器温度 */
        int16_t temperature = adc_get_temperature_from_dma();
        printf("TEMP     : %.1f°C\r\n", temperature / 100.0f);
        
        printf("---------------------------------\r\n");
    }
}
